#include "ex03.cpp"
#include <stdio.h>
#include <unistd.h>

pw::semaphore ready(0), sem1(0), sem2(0);
bool stop = false;

// [[ex03fa]]
// DEBUG descr Split lock.
class splitlock
{
public:
	// @param safe - flag: if true, place the lock
	// initially into a safe state, so that the worker
	// thread may not enter it until the control
	// thread releases it; if false, place it into the
	// unsafe state and allow the worker thread to
	// enter it freely
	splitlock(bool safe = false) :
		cond_safe_(cond_enter_),
		may_enter_(!safe),
		is_safe_(safe)
	{ }

	// Control thread side of API.

	void request_safe()
	{
		pw::lockmutex lm(cond_enter_);
		may_enter_ = false;
	}

	void wait_safe()
	{
		pw::lockmutex lm(cond_enter_);
		while (!is_safe_) {
			cond_safe_.wait();
		}
	}

	// A convenience combination - just in case
	void lock_safe()
	{
		request_safe();
		wait_safe();
	}

	void release_safe()
	{
		pw::lockmutex lm(cond_enter_);
		may_enter_ = true;
		cond_enter_.broadcast();
	}

	// Worker thread side of API.

	void enter()
	{
		pw::lockmutex lm(cond_enter_);
		while (!may_enter_)
			cond_enter_.wait();
		is_safe_ = false;
	}

	void leave()
	{
		pw::lockmutex lm(cond_enter_);
		is_safe_ = true;
		cond_safe_.broadcast();
	}

protected:
	// signaled when the worker thread is allowed to
	// enter
	pw::pmcond cond_enter_; 
	// signaled when the control thread becomes safe
	pw::pchaincond cond_safe_; 
	// flag: the worker thread may enter
	bool may_enter_; 
	// flag: the control thread is safe (working
	// thread has not entered)
	bool is_safe_; 
};
// END

// [[ex03fb]]
// DEBUG descr A worker thread using a split lock.
class worker : public pw::pwthread
{
public:
	worker() :
		split_(true)
	{ }
	// ...
	void *execute()
	{
		bool done = false;
		// ... initialize ...
	// DEBUG {
		ready.signal();
	// DEBUG }
		while(!done)
		{
			// ... get next request ...
	// DEBUG {
			sem1.wait();
	// DEBUG }
			split_.enter();
			// ... process the request ...
	// DEBUG {
			fprintf(stderr, "worker entered\n");
			sem2.wait();
			done = stop;
			fprintf(stderr, "worker leaving\n");
	// DEBUG }
			split_.leave();
		}
		return 0;
	}

protected:
	// DEBUG {
public:
	// DEBUG }
	splitlock split_;
};
// END

// DEBUG {
int main()
{
	worker w;
	w.start();
	ready.wait();

	w.split_.lock_safe();
	fprintf(stderr, "locked safe\n");
	w.split_.release_safe();
	fprintf(stderr, "released safe\n");

	w.split_.request_safe();
	sem1.signal(2);
	sem2.signal(1);
	w.split_.wait_safe();
	fprintf(stderr, "locked safe\n");
	w.split_.release_safe();
	fprintf(stderr, "released safe\n");
	usleep(100*1000);
	
	w.split_.request_safe();
	sem2.signal(1);
	w.split_.wait_safe();
	fprintf(stderr, "locked safe\n");
	w.split_.release_safe();
	fprintf(stderr, "released safe\n");
	usleep(100*1000);

	stop = true;
	sem1.signal(1);
	sem2.signal(1);

	w.join();
	return 0;
}
// DEBUG }
/* Sample output:

locked safe
released safe
locked safe
released safe
worker entered
worker leaving
worker entered
worker leaving
locked safe
released safe
worker entered
worker leaving

*/
